package model.arbolBSharp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Nodo {
	
	private List<String> listaClaves;
	private List<Integer> listaPunteros;
	private Integer nivel;
	private Integer posicionEnArchivo;
	
	public void setPosicionEnArchivo(Integer posicionEnArchivo) {
		this.posicionEnArchivo = posicionEnArchivo;
	}

	public Nodo(){
		listaPunteros = new ArrayList<Integer>();
		listaClaves = new ArrayList<String>();		
	}

	public Nodo(List<String> listaPrefijos, List<Integer> listaPunteros2,
			int posicion, int nivel) {
		this.listaClaves =listaPrefijos;		
		this.listaPunteros = listaPunteros2;		
		this.posicionEnArchivo = new Integer(posicion);
		this.nivel = new Integer(nivel);
	}

	public List<String> getListaClaves() {
		return listaClaves;
	}

	public List<Integer> getListaPunteros() {
		return listaPunteros;
	}

	public Integer getNivel() {
		return nivel;
	}

	public void setNivel(Integer nivel) {
		this.nivel = nivel;
	}
	
	public Integer getPosicionEnArchivo() {
		return posicionEnArchivo;
	}
	
	/**
	 * Si el texto es mayor que todas las claves se devuelve el puntero de la ultima clave
	 * 
	 * @param clave
	 * @return
	 */
	public int buscarPuntero(String clave) {
		int pos = Collections.binarySearch(listaClaves, clave);
		if (pos<0){
			pos = -1*(pos+1);
		}
		if(pos==listaClaves.size())
			pos--;
		
		return listaPunteros.get(pos);
	}

	/**
	 * Devuelve el vecino izquierdo del puntero. Si el puntero no esta en el nodo devuelve 0.
	 * @param puntero
	 * @return
	 * @throws ArbolBSharpException
	 */
	public int getVecinoIzquierdo(Integer puntero) throws ArbolBSharpException {
		for(int i=0; i<listaPunteros.size(); i++){
			if(listaPunteros.get(i).equals(puntero)){
				if(i>0){
					return listaPunteros.get(i - 1);
				} else {
					return 0;
				}
			}				
		}
		throw new ArbolBSharpException("No existe el puntero en el nodo");
	}

	public int getVecinoDerecho(Integer puntero) throws ArbolBSharpException {
		for(int i=0; i<listaPunteros.size(); i++){
			if(listaPunteros.get(i).equals(puntero)){
				if(i<(listaPunteros.size()-1)){
					return listaPunteros.get(i + 1);
				} else {
					return 0;
				}
			}				
		}
		throw new ArbolBSharpException("No existe el puntero en el nodo");
	}

	public void reemplazarClave(String claveMayorPrevia, String texto) throws ArbolBSharpException {
		int pos = Collections.binarySearch(listaClaves, claveMayorPrevia);
		if (pos<0)
			throw new ArbolBSharpException("No existe la claveMayorPrevia: "+texto+". Para reemplazar: "+claveMayorPrevia);
		listaClaves.remove(pos);
		listaClaves.add(pos, texto);		
	}

	
	/**
	 * Quita el maximo prefijo y su puntero derecho
	 */
	public void quitarMaximoPrefijo() {
		this.listaClaves.remove(this.listaClaves.size()-1);
		this.listaPunteros.remove(this.listaPunteros.size()-1);		
	}
	
	public int cantidadPrefijos() {
		return this.listaClaves.size();
	}

	/**
	 * Agrega una clave y su puntero izquierdo
	 * 
	 * @param clave
	 * @param punteroIzquierdo
	 * @throws NodoException 
	 */
	public void agregarPrefijo(String clave, int punteroIzquierdo) throws NodoException {
		int pos = Collections.binarySearch(listaClaves, clave);
		if (pos>=0){
			throw new NodoException("El prefijo ya existe en el arbol");
		} else {
			listaClaves.add(-1*(pos + 1), clave);
			listaPunteros.add(-1*(pos + 1), punteroIzquierdo);
		}	
	}

	public void quitarTodos() {
		this.listaClaves.clear();
		this.listaPunteros.clear();		
	}

	/*
	 * Recive el nodoD con las claves del nodo a la derecha del nodo a dividir.
	 * Divide las claves entre tres nodos.
	 * nodoDNuevo es nodo vacio que sera el futuro nodo a la derecha  
	 */
	public void dividirNodo(Nodo nodoD, Nodo nodoDNuevo) throws ArbolBSharpException {
		
		int cantElem = this.listaClaves.size() + nodoD.cantidadPrefijos();
		
		List<String> listaPref = new ArrayList<String>(cantElem);		
		listaPref.addAll(this.listaClaves);
		listaPref.addAll(nodoD.getListaClaves());
		
		List<Integer> listaPunt = new ArrayList<Integer>(cantElem);
		listaPunt.addAll(this.listaPunteros);
		listaPunt.addAll(nodoD.getListaPunteros());

		int i = cantElem;
		
		while(i>cantElem*2/3){
			i--;
			nodoDNuevo.agregarPrimero(listaPref.get(i), listaPunt.get(i));
		}
		
		nodoD.quitarTodos();		
		while(i>cantElem/3){
			i--;
			nodoD.agregarPrimero(listaPref.get(i), listaPunt.get(i));
		}
		
		this.quitarTodos();
		while(i>0){
			i--;
			this.agregarPrimero(listaPref.get(i), listaPunt.get(i));
		}		

	}

	public void agregarPrimero(String pref, Integer punt) {
		this.listaClaves.add(0, pref);
		this.listaPunteros.add(0, punt);		
	}
	/*
	 * Obtiene el puntero al menor hoja 
	 */
	public Integer getPunteroAMenor() {
		return this.listaPunteros.get(0);
	}

	public Integer getPunteroAMayor() {
		// TODO Auto-generated method stub
		return this.listaPunteros.get(this.listaPunteros.size()-1);
	}
	/*
	 *Devuelve la mayor clave del nodo 
	 */
	public String getMayorClave() throws ArbolBSharpException {
		if(listaClaves.size()==0)
			throw new ArbolBSharpException("El nodo esta vacio");
		
		return listaClaves.get(listaClaves.size()-1);
	}
	
	public String getMenorClave() throws ArbolBSharpException {
		if(listaClaves.size()==0)
			throw new ArbolBSharpException("El nodo esta vacio");
		
		return listaClaves.get(0);
	}
	
	/*
	 *Remueve la clave y su respectivo puntero a nodo 
	 */
	public boolean removerClave(String clave) throws NodoException {
		int pos = Collections.binarySearch(listaClaves, clave);
		if (pos<0){
			return false;
		} else {
			listaClaves.remove(pos);
			listaPunteros.remove(pos);
			return true;
		}
		
	}
	/*
	 *Devuelve true si la clave que se le pasa por parametro es la clave maxima del nodo 
	 */
	public boolean esClaveMaxima(String texto) {
		int ultimaPos = this.listaClaves.size()-1;
		if (this.listaClaves.get(ultimaPos).equals(texto))
			return true;
		return false;
	}
}